# 泛型
- 泛型是 JDK1.5 之后引入的新特性,主要目的是解决类对象的安全隐患问题,泛型提供了编译时类型安全检测机制
- 泛型只在编译阶段有效
- 泛型的本质是参数化类型,类中的属性类型或方法的参数类型可以在类的使用时,动态决定
没有泛型前,Object
类型可以充当泛型,但是并不意味着 Object
是万能的
class Point {
Object x;
public Point(Object x) {
this.x = x;
}
public Object getX() {
return this.x;
}
}
Point p = new Point(10); // 自动装箱 -> 接着向上转型
int n = (Integer)p.getX(); // 强制向下转型 -> 接着自动拆箱
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
利用 Object
类型,此时的设计虽然满足需求,但是存在安全隐患
/*
* 此时代码在使用上存在错误
* 但在编译时不存在语法错误
* 实际运行中则会出现错误'ClassCastException'
*/
Point p = new Point(2.4);
int n = (Integer)p.getX();
1
2
3
4
5
6
7
2
3
4
5
6
7
# 泛型类
- 泛型的本质是参数化类型,但是参数类型只能是引用数据类型,不能是基本类型
- 使用泛型时,如果没有指定泛型类型,默认使用
Object
类型进行处理
class Point<T> {
T x;
public Point(T x) {
this.x = x;
}
public T getX() {
return this.x;
}
}
Point<Integer> p = new Point<Integer>(10);
int n = p.getX();
// 不指定泛型类型,默认使用 Object 类型
Point p = new Point(10);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 泛型通配符
# 问题的出现
/*
* processPoint 是否可以处理 Point<Integer> 类型?
* processPoint 如果希望处理更多的类型,如 Point<Double> ?
*/
public void processPoint(Point<Number> p) {
System.out.println(p.getX());
}
// 错误:Point<java.lang.Integer> cannot be applied to Point<java.lang.Number>
Point<Integer> p = new Point<Integer>(10);
processPoint(p);
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 虽然 Number 是 Integer 的父类,但是
Point<Integer>
不能被看作为Point<Number>
的子类 - 那么是否可以使用方法重载呢,有两个问题
- 引用类型的个数几乎无穷,如果每一个都需要重载,非常低效
Java
不支持方案预想的重载,因为Point<Integer>
和Point<Double>
都将被视为Point
类型,不满足重载要求
# 通配符?
类型通配可以使用 ?
代替具体的类型参数
此处 ?
是类型实参,而不是类型形参,即此处的 ?
和 Number、Integer 一样都是一种实际的类型,可以把 ?
看成所有类型的父类,是一种真实的类型
public void processPoint(Point<?> p) {
System.out.println(p.getX());
}
1
2
3
2
3
注意
使用通配符时,意味着对泛型类型是未知的,因此在方法内部也不能随意类型相关的操作(可以执行 Object 相关操作)
public void processPoint(Point<?> p) {
// error: incompatible types: CAP#1 cannot be converted to int
int n = p.getX();
p.x = 5;
}
1
2
3
4
5
2
3
4
5
# 通配符的应用
- 设置泛型的下限:
<? super 'class'>
<? super String>
: 表示可以设置的泛型类只能是String
以及其父类- 不能用于泛型类定义
- 设置泛型的上限:
<? extends 'class'>
<? extends Number>
: 表示可以设置的泛型类型只能是Number
以及其子类- 可以用于泛型类定义
# 泛型方法
泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型
- 泛型方法并不一定要定义在泛型类或者泛型接口中
- 任意方法都可以为泛型方法,但是需要对泛型标记加以声明
/**
* 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
* 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
* 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
class GenerateTest<T>{
/**
* 虽然在方法中使用了泛型,但是这并不是一个泛型方法
* 这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型
*/
public void show1(T t){
System.out.println(t.toString());
}
// 在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型
// 可以类型与T相同,也可以不同
// 由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,
// 编译器也能够正确识别泛型方法中识别的泛型
public <E> void show2(E t){
System.out.println(t.toString());
}
// 在泛型类中声明了一个泛型方法,使用泛型T,
// 注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型
public <T> void show3(T t){
System.out.println(t.toString());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 泛型接口
泛型不仅可以用于类,也可以用在接口上
interface IMessage<T> {
void send(T content);
}
1
2
3
2
3
子类实现泛型接口通常有两种方法
1. 子类未传入泛型实参,也使用泛型类型
/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即: MessageImpl<T> implements IMessage<T>
* 如果不声明泛型,如:class MessageImpl implements IMessage<T>,编译器会报错:"Unknown class"
*/
class MessageImpl<T> implements IMessage<T> {
public void send(T content) {
System.out.println(content);
}
}
IMessage<T> msg = new MessageImpl<T>();
/*
* JDK1.8引入了自动推导泛型的概念
* 如果前面已经定义泛型类型,后面不需要重复定义
*/
IMessage<T> msg = new MessageImpl<>();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2. 子类直接确认泛型实参类型
/**
* 在实现类实现泛型接口时,如已确认泛型实参,则所有使用泛型的地方都要替换成传入的实参类型
*/
class MessageImpl implements IMessage<String> {
public void send(String content) {
System.out.println(content);
}
}
IMessage<String> msg = new MessageImpl();
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 静态方法与泛型
如果希望在类中的静态方法使用泛型,由于静态方法无法访问类上定义的泛型,因此如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上
即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法
public class StaticMethod {
public static <T> void test(T arg) {
}
}
1
2
3
4
5
2
3
4
5
# 内部类和泛型
- 对于非静态内部类,会自动继承外围类的泛型参数
- 静态内部类不会自动继承外围类泛型参数